﻿#include <Sys\RenderUnit\SysRenderDatagram\LIGHTSOURCERD.HPP>
#include <Render\Render_Win32\Render_Win32_OpenGL\RENDER_WIN32_OPENGL.HPP>
#include <Sys\RenderUnit\SYSDDCLASSID.HPP>
#include <Sys\MATH.HPP>

using namespace CxxlMan;
using namespace Cxxl3D;

class CLightSourceMgrDD_OpenGL:public IDatagramDisposal_OpenGL, public ILightSourceVisitor, public ILightSourceMgrBridge, public ICameraLoopReset
{

  // 記錄和光源所配對的 LightID
  struct tagLightSourcMap:public cxxlObject
  {
    int LightID;
    Smart_Ptr<ILightSource> ILightSource_Ptr; // 保存作為 Key 的光源，以確保 Key 一定存在

    // Constructor
    tagLightSourcMap(int ID, const Smart_Ptr<ILightSource> &ILightSource_Arg)
      :cxxlObject(Spirit_Easy),
       ILightSource_Ptr(ILightSource_Arg,this)
    {
      LightID = ID;
    }
  };

  class LightSourcMapTree:public cxxlAVL_Tree<tagLightSourcMap,ILightSource*,true>
  {
    // 排序檢測，延伸類別須實作此函數
    // 要讓 O_Key 鍵值所代表的物件排在 左 回答 true，否則 false
    bool cxxlFASTCALL isLeftAt(ILightSource* O_Key,ILightSource* addKey) const
    {
      return O_Key <= addKey;
    } 
   
    // 這函數用來決定在容器中 O_Key 鍵值所代表的物件，是不是 findKey 所要尋找的 
    // 是 回答 true，否則 false，延伸類別須實作此函數
    bool cxxlFASTCALL isEqual(ILightSource* O_Key,ILightSource* findKey)  const
    {
      return O_Key == findKey;
    } 
   
  public:
    LightSourcMapTree(ISpirit *spirit = Spirit_Easy)  // Constructor
     :cxxlObject(spirit)
    {
    }
  }TakeTree; // 已使用的 tagLightSourcMap

  cxxlList<tagLightSourcMap> WaitingList; // 可用的 tagLightSourcMap


  // 移除光源
  void cxxlFASTCALL DelLightSource(const Smart_Ptr<cxxlList<ILightSource> > &DelILightSource_List)
  {
    DelILightSource_List->ResetPT(toHead);
    unsigned long Count = DelILightSource_List->GetCount();
    unsigned long n = 0;
    for(Smart_Ptr<ILightSource> ILightSource_Ptr = (*DelILightSource_List)++; n < Count; ILightSource_Ptr = (*DelILightSource_List)++,++n)
    {
      Smart_Ptr<tagLightSourcMap> tagLightSourcMap_Ptr = TakeTree.Pump(ILightSource_Ptr);
			glDisable(tagLightSourcMap_Ptr->LightID); // 關閉這盞光
      tagLightSourcMap_Ptr->ILightSource_Ptr.Destroy();
      WaitingList.Push_Back(tagLightSourcMap_Ptr);
    }
  }

  // 加入光源
  void cxxlFASTCALL AddLightSource(const Smart_Ptr<cxxlList<ILightSource> > &NewILightSource_List)
  {
    NewILightSource_List->ResetPT(toHead);
    unsigned long Count = NewILightSource_List->GetCount();
    unsigned long n = 0;
    for(Smart_Ptr<ILightSource> ILightSource_Ptr = (*NewILightSource_List)++; n < Count; ILightSource_Ptr = (*NewILightSource_List)++,++n)
    {
      Smart_Ptr<tagLightSourcMap> tagLightSourcMap_Ptr = WaitingList.Pop_Back();
      tagLightSourcMap_Ptr->ILightSource_Ptr = ILightSource_Ptr;
      TakeTree.Add(tagLightSourcMap_Ptr,ILightSource_Ptr);

      LightID = tagLightSourcMap_Ptr->LightID;			
      glEnable(LightID); // 打開這盞光
      ILightSource_Ptr->Accept(*this);
    }
  }

  // 須實作，每當一個 ICamera 循環前會呼叫一次
  virtual void cxxlFASTCALL Reset()
  {
    m_ILightSourceMgrRD_Ptr.Destroy();
    m_ILightSource_List.Destroy();

    Smart_Ptr<cxxlList<tagLightSourcMap> > tagLightSourcMap_List = TakeTree.cxxlList_Create();
    TakeTree.ClearAll();

    while(true)
    {
      Smart_Ptr<tagLightSourcMap> tagLightSourcMap_Ptr = tagLightSourcMap_List->Pop_Front();
      if(tagLightSourcMap_Ptr.isNULL()) break;

			glDisable(tagLightSourcMap_Ptr->LightID); // 關閉這盞光
      tagLightSourcMap_Ptr->ILightSource_Ptr.Destroy();
      WaitingList.Push_Back(tagLightSourcMap_Ptr);
    }
  }

  // 只是作為有沒呼叫過 IRenderDatagramDisposalCenter_Arg->AddICameraLoopReset(this) 的辨識用
  IRenderDatagramDisposalCenterOpengl *m_pIRenderDatagramDisposalCenter;


  Smart_Ptr<ILightAmbient> m_ILightAmbient_Ptr;
  Smart_Ptr<ILightSourceMgrRD> m_ILightSourceMgrRD_Ptr;
  Smart_Ptr<cxxlList<ILightSource> > m_ILightSource_List;

  unsigned int  LightID;
  CSmart_Ptr<Matrix4x4> m_ViewMatrix_cPtr;


  void cxxlFASTCALL doLight(const C3DVector4 &Specular, const C3DVector4 &Ambient, const C3DVector4 &Color, const C3DVector4 &Position, const C3DVector4 &Attenuation,
    const C3DVector4 &SpotDirectional,
    const C3D_FLOAT &SpotlightCutoff, const C3D_FLOAT &SpotlightExponent)
  {

    // 光源位置、方向
    // 把一個 (x,y,z,w) 的向量傳入 GL_POSITION, 如果 w 值是 0 會被視為是方向
    // 方向的定義跟 Direct3D 相反
    glLightfv(LightID, GL_POSITION, (float *)&Position);

    // 隨距離的衰減值
    // 1/(CONSTANT + LINEAR*d + QUADRATIC*d^2) 公式中的 CONSTANT、LINEAR、QUADRATIC 值
    glLightf(LightID, GL_CONSTANT_ATTENUATION,	Attenuation[0]);
    glLightf(LightID, GL_LINEAR_ATTENUATION,	Attenuation[1]);
    glLightf(LightID, GL_QUADRATIC_ATTENUATION, Attenuation[2]);

    // 光柱的方向
    glLightfv(LightID, GL_SPOT_DIRECTION, (float *)&SpotDirectional);

    // 光柱圓錐的一半的角度
    glLightf(LightID, GL_SPOT_CUTOFF, SpotlightCutoff);

    // 光柱圓錐的角度衰減
    glLightf(LightID, GL_SPOT_EXPONENT, SpotlightExponent);


    // 光源的 Ambient 值
    glLightfv(LightID, GL_AMBIENT, (float *)&Ambient); 

    // 光源的 Diffuse 值
    glLightfv(LightID, GL_DIFFUSE, (float *)&Color);

    // 光源的 Specular 值
    glLightfv(LightID, GL_SPECULAR, (float *)&Specular);
  }


  virtual void cxxlFASTCALL VisitLightDirectional(const Smart_Ptr<ILightDirectional> &ILightDirectional_Arg)
  {
    Smart_Ptr<IC3D_Node> IC3D_Node_Ptr = ILightDirectional_Arg->Get_IC3D_Node();
    CSmart_Ptr<const Matrix4x4> WorldMatrix_cPtr = IC3D_Node_Ptr->GetWorldMatrix();
    Matrix4x4 WorldViewMatrix = (*WorldMatrix_cPtr) * (*m_ViewMatrix_cPtr);
    //glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf((C3D_FLOAT*)(Matrix4x4*)&WorldViewMatrix); // Set Modeview matrix


    C3DVector4 Specular, Ambient, Color, Directional;

    ILightDirectional_Arg->GetData(Specular, Ambient, Color, Directional);

    // 方向的定義跟 Direct3D 相反, 所以要加個負號.
    doLight(Specular, Ambient, Color, (-Directional), 
      C3DVector4(1,0,0,0), 
      C3DVector4(0,0,0,0), 
      180, 0);
  }

  virtual void cxxlFASTCALL VisitLightPoint(const CxxlMan::Smart_Ptr<ILightPoint> &ILightPoint_Arg)
  {
    Smart_Ptr<IC3D_Node> IC3D_Node_Ptr = ILightPoint_Arg->Get_IC3D_Node();
    CSmart_Ptr<const Matrix4x4> WorldMatrix_cPtr = IC3D_Node_Ptr->GetWorldMatrix();
    Matrix4x4 WorldViewMatrix = (*WorldMatrix_cPtr) * (*m_ViewMatrix_cPtr);
    //glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf((C3D_FLOAT*)(Matrix4x4*)&WorldViewMatrix); // Set Modeview matrix


    C3DVector4 Specular, Ambient, Color, Position, Attenuation;

    ILightPoint_Arg->GetData(Specular, Ambient, Color, Position, Attenuation);

    doLight(Specular, Ambient, Color, Position, Attenuation, C3DVector4(0,0,0,0), 180, 0);
  }

  virtual void cxxlFASTCALL VisitLightSpot(const CxxlMan::Smart_Ptr<ILightSpot> &ILightSpot_Arg)
  {
    Smart_Ptr<IC3D_Node> IC3D_Node_Ptr = ILightSpot_Arg->Get_IC3D_Node();
    CSmart_Ptr<const Matrix4x4> WorldMatrix_cPtr = IC3D_Node_Ptr->GetWorldMatrix();
    Matrix4x4 WorldViewMatrix = (*WorldMatrix_cPtr) * (*m_ViewMatrix_cPtr);
    //glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf((C3D_FLOAT*)(Matrix4x4*)&WorldViewMatrix); // Set Modeview matrix


    C3DVector4 Specular, Ambient, Color, Directional, Position, Attenuation;
    C3D_FLOAT SpotlightCutoff, SpotlightExponent;    

    ILightSpot_Arg->GetData(Specular, Ambient, Color, Directional, Position, Attenuation, SpotlightCutoff, SpotlightExponent);

    doLight(Specular, Ambient, Color, Position, Attenuation, Directional, SpotlightCutoff, SpotlightExponent);


  }

  virtual bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagramDisposalCenterOpengl> &IRenderDatagramDisposalCenter_Arg,
    const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, const CxxlMan::CSmart_Ptr<Matrix4x4> &ViewMatrix_Arg)
  {
    if(m_pIRenderDatagramDisposalCenter != (IRenderDatagramDisposalCenterOpengl *)IRenderDatagramDisposalCenter_Arg)
    {
      m_pIRenderDatagramDisposalCenter = (IRenderDatagramDisposalCenterOpengl *)IRenderDatagramDisposalCenter_Arg;
      IRenderDatagramDisposalCenter_Arg->AddICameraLoopReset(this);
    }

    Smart_Ptr<ILightSourceMgrRD> ILightSourceMgrRD_Ptr = (ILightSourceMgrRD*)GetDefaultInterface(IRenderDatagram_Arg);

    if((ILightSourceMgrRD*)m_ILightSourceMgrRD_Ptr == (ILightSourceMgrRD*)ILightSourceMgrRD_Ptr)
    {
      if(isModified(m_ILightSourceMgrRD_Ptr) == false)
        return true;
    }
    else
    {
      isModified(ILightSourceMgrRD_Ptr);
      m_ILightSourceMgrRD_Ptr = ILightSourceMgrRD_Ptr;
    }

    Smart_Ptr<ILightAmbient> ILightAmbient_Ptr(NULL);
    ILightSourceMgrRD_Ptr->GetAmbient(ILightAmbient_Ptr);
    if((ILightAmbient*)m_ILightAmbient_Ptr != (ILightAmbient*)ILightAmbient_Ptr)
    {
      m_ILightAmbient_Ptr = ILightAmbient_Ptr;
      C3DVector4 Ambient;
      ILightAmbient_Ptr->GetData(Ambient);

      // 設定環境光
      glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (float *)&Ambient);
    }


    m_ViewMatrix_cPtr = ViewMatrix_Arg;

    Smart_Ptr<cxxlList<ILightSource> > NewILightSource_List = ILightSourceMgrRD_Ptr->GetILightSources();
    Smart_Ptr<cxxlList<ILightSource> > OldILightSource_List = m_ILightSource_List;
    m_ILightSource_List = NewILightSource_List;


    if(OldILightSource_List.isNULL() == false)
    {
      OldILightSource_List->ResetPT(toHead);
      unsigned long OldCount = OldILightSource_List->GetCount();

      // OldILightSource_List 中的元素不存在於 NewILightSource_List 放入這
      Smart_Ptr<cxxlList<ILightSource> > DelILightSource_List = new cxxlList<ILightSource>; 

      unsigned long i = 0;
      for(Smart_Ptr<ILightSource> OldILightSource_Ptr = (*OldILightSource_List)++; i < OldCount; OldILightSource_Ptr = (*OldILightSource_List)++, ++i)
      {
        bool isSame = false; // 有找到改為 true

        // NewILightSource_List 中的元素不存在於 OldILightSource_List 放入這
        Smart_Ptr<cxxlList<ILightSource> > AddILightSource_List = new cxxlList<ILightSource>;

        NewILightSource_List->ResetPT(toHead);
        unsigned long NewCount = NewILightSource_List->GetCount();

        unsigned long j = 0;
        for(Smart_Ptr<ILightSource> NewILightSource_Ptr = (*NewILightSource_List)++; j < NewCount; NewILightSource_Ptr = (*NewILightSource_List)++, ++j)
        {
          if((ILightSource*)OldILightSource_Ptr == (ILightSource*)NewILightSource_Ptr)
          {
            isSame = true;
            while(++j < NewCount)
              AddILightSource_List->Push_Back((*NewILightSource_List)++); // 找到相同之後把其餘都放入
          }
          else
            AddILightSource_List->Push_Back(NewILightSource_Ptr); // 把不相同的放入
        }

        // 沒找到放入待刪 list
        if(isSame == false)
          DelILightSource_List->Push_Back(OldILightSource_Ptr);

        NewILightSource_List = AddILightSource_List; // 準備下一迴圈再找
      }

      DelLightSource(DelILightSource_List);
    }

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix(); // 保存 world view matrix
    AddLightSource(NewILightSource_List);
    glPopMatrix(); // 恢復 world view matrix

    return true;

  }

public:

  // Consteructor
  CLightSourceMgrDD_OpenGL()
    :cxxlObject(Spirit_Easy),
    m_ILightAmbient_Ptr(NULL,this),
    m_ILightSourceMgrRD_Ptr(NULL,this),
    m_ILightSource_List(NULL,this),
    m_ViewMatrix_cPtr((Cxxl3D::Matrix4x4*)NULL)
  {
    unsigned int ID = 0;
    m_pIRenderDatagramDisposalCenter = NULL;
    
    while(ID++ < 8)
      WaitingList.Push_Front(new tagLightSourcMap(GL_LIGHT0+ID,(ILightSource*)NULL));
  }

  // Desteructor
  virtual ~CLightSourceMgrDD_OpenGL()
  {
  }

};

cxxlObject *cxxlFASTCALL New_CLightSourceMgrDD_OpenGL()
{
  return new CLightSourceMgrDD_OpenGL;
}

